package com.xutianpeng.disk.file.service.io;

import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.ObjectListing;
import com.obs.services.model.ObsObject;
import com.obs.services.model.TemporarySignatureRequest;
import com.obs.services.model.TemporarySignatureResponse;
import com.xutianpeng.disk.file.service.FileIoService;
import com.xutianpeng.disk.file.service.FileService;
import com.xutianpeng.disk.mybatis.entity.PtFileEntity;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Service
public class FileIoServiceImpl implements FileIoService {
    @Value("${end.point}")
    private String endpoint = "";
    @Value("${ak}")
    private String ak = "";
    @Value("${sk}")
    private String sk = "";
    @Value("${bucket.name}")
    private String bucketName = "";

    private ObsClient obsClient = null;
    @Resource
    private FileService fileService;
    private Logger logger = LogManager.getLogger(this.getClass());

    private ObsClient getObsClient() {
        if (this.obsClient == null) {
            this.obsClient = new ObsClient(ak, sk, endpoint);
        }
        return this.obsClient;
    }

    /**
     * 数据同步：将OBS中的文件信息同步到数据库
     */
    @Override
    public void syncFileData() {
        ObjectListing list = getObsClient().listObjects(bucketName);
        List<ObsObject> listObjects = list.getObjects();
        PtFileEntity ptFileEntity = null;
        List<PtFileEntity> ptFileEntityList = new ArrayList<>();
        List<PtFileEntity> ptFileEntityList1 = fileService.queryFileListAll();
        String uuid = "";

        for (ObsObject obsObject : listObjects) {
            ptFileEntity = new PtFileEntity();
            ptFileEntityList.add(ptFileEntity);
            ptFileEntity.setParentUuid("-1");
            ptFileEntity.setFilePath(obsObject.getObjectKey());
            uuid = UUID.randomUUID().toString();

            // 复用已有UUID（避免重复生成）
            for (PtFileEntity ptFileEntityItem : ptFileEntityList1) {
                if (obsObject.getObjectKey().equals(ptFileEntityItem.getFilePath())) {
                    uuid = ptFileEntityItem.getFileUuid();
                    break;
                }
            }

            ptFileEntity.setFileUuid(uuid);
            ptFileEntity.setFileCapacity(obsObject.getMetadata().getContentLength());

            // 日志记录对象存储关键信息
            logger.info("obsClient obsKey:" + obsObject.getObjectKey()
                    + "  obsClient obsSize:" + obsObject.getMetadata().getContentLength()
                    + "  obsClient isFolder:" + isFolder(obsObject.getObjectKey()));

            // 判断文件类型（文件夹/文件）
            if (isFolder(obsObject.getObjectKey())) {
                ptFileEntity.setFileType(FILE_TYPE_FOLDER);
            } else {
                ptFileEntity.setFileType(FILE_TYPE_FILE);
            }

            // 设置文件名
            ptFileEntity.setFileName(getFileName(obsObject.getObjectKey()));
            logger.info(ptFileEntity);
        }

        // 构建父子关系
        for (PtFileEntity child : ptFileEntityList) {
            for (PtFileEntity parent : ptFileEntityList) {
                if (child.getFilePath().equals(parent.getFilePath())) {
                    continue; // 跳过自身
                }
                if (FILE_TYPE_FILE == parent.getFileType()) {
                    continue; // 父节点必须是文件夹
                }
                // 子路径包含父路径时建立关联
                if (child.getFilePath().indexOf(parent.getFilePath()) != -1) {
                    child.setParentUuid(parent.getFileUuid());
                }
            }
        }

        logger.info(ptFileEntityList);
        fileService.saveFileInfo(ptFileEntityList);
    }

    /**
     * 创建文件夹
     */
    @Override
    public void mkdirFileInfo(String parentId, String folderName) {
        PtFileEntity entity = fileService.queryFileInfoById(parentId);
        String fileName = folderName;
        if (entity != null) {
            fileName = entity.getFilePath() + folderName;
        }
        fileName = fileName + FILE_SEPARATOR; // 文件夹路径以分隔符结尾
        getObsClient().putObject(bucketName, fileName, new ByteArrayInputStream(new byte[0]));
        syncFileData(); // 同步数据到数据库
    }

    /**
     * 上传文件
     */
    @Override
    public void uploadFile(String parentId, String fileName, InputStream inputStream) {
        PtFileEntity entity = fileService.queryFileInfoById(parentId);
        String fileNameU = fileName;
        if (entity != null) {
            fileNameU = entity.getFilePath() + fileName; // 拼接父路径
        }
        getObsClient().putObject(bucketName, fileNameU, inputStream);
        syncFileData(); // 同步数据到数据库
    }

    /**
     * 将文件移至回收站（文档1独有功能）
     */
    @Override
    public void deleteToRecycle(String fileUuid) {
        try {
            // 查询文件信息
            PtFileEntity fileEntity = fileService.queryFileInfoById(fileUuid);
            if (fileEntity == null) {
                logger.error("文件不存在，fileUuid: {}", fileUuid);
                throw new IllegalArgumentException("文件不存在");
            }

            // 更新数据库删除标识
            fileService.updateFileToRecycle(fileUuid);
            logger.info("文件已标记为回收站状态，fileUuid: {}", fileUuid);

            // 可选：移动对象存储中的文件到回收站路径
            // String recyclePath = "recycle/" + fileEntity.getFilePath();
            // getObsClient().copyObject(bucketName, fileEntity.getFilePath(), bucketName, recyclePath);
            // getObsClient().deleteObject(bucketName, fileEntity.getFilePath());
        } catch (ObsException e) {
            logger.error("对象存储操作失败，fileUuid: {}", fileUuid, e);
            throw new RuntimeException("文件操作失败", e);
        } catch (Exception e) {
            logger.error("删除文件到回收站失败，fileUuid: {}", fileUuid, e);
            throw new RuntimeException("删除文件失败", e);
        }
    }

    /**
     * 复制文件/文件夹
     */
    @Override
    public void copyFile(String parentId, String srcUuidList) {
        PtFileEntity entity = fileService.queryFileInfoById(parentId);
        String folder = "";
        if (entity != null) {
            folder = entity.getFilePath(); // 目标父文件夹路径
        }

        String[] fileUuidArray = srcUuidList.split(","); // 拆分源文件UUID列表
        String filePath = "";
        ObsObject obsObject;
        InputStream inputStream;

        for (String fileUuid : fileUuidArray) {
            entity = fileService.queryFileInfoById(fileUuid);
            if (entity != null) {
                filePath = folder + entity.getFileName(); // 目标路径 = 父路径 + 文件名

                // 处理文件夹/文件复制
                if (FILE_TYPE_FOLDER.equals(entity.getFileType())) {
                    // 复制文件夹（创建空文件夹）
                    getObsClient().putObject(bucketName, filePath, new ByteArrayInputStream(new byte[0]));
                } else {
                    // 复制文件（读取源文件流并重传）
                    obsObject = getObsClient().getObject(bucketName, entity.getFilePath());
                    inputStream = obsObject.getObjectContent();
                    getObsClient().putObject(bucketName, filePath, inputStream);
                }
            }
        }

        syncFileData(); // 同步数据到数据库
    }

    /**
     * 文件下载（文档2独有功能）
     */
    @Override
    public ResponseEntity<byte[]> downFile(String fileUuid) {
        PtFileEntity entity = fileService.queryFileInfoById(fileUuid);
        if (entity == null) {
            logger.error("文件不存在：" + fileUuid);
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }

        HttpHeaders headers = new HttpHeaders();
        // 告知浏览器以下载方式打开
        headers.setContentDispositionFormData("attachment", entity.getFileName());
        // 设置MIME类型为二进制流
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

        ObsObject obsObject = getObsClient().getObject(bucketName, entity.getFilePath());
        try (InputStream inputStream = obsObject.getObjectContent();
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {

            byte[] block = new byte[1024];
            int length;
            while ((length = inputStream.read(block)) > 0) {
                byteArrayOutputStream.write(block, 0, length);
                byteArrayOutputStream.flush();
            }

            return new ResponseEntity<>(byteArrayOutputStream.toByteArray(), headers, HttpStatus.CREATED);
        } catch (IOException e) {
            logger.error("文件下载失败：" + e.getMessage(), e);
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 获取默认有效期（1小时）的分享链接
     */
    @Override
    public String getShareUrl(String fileUuid) {
        PtFileEntity entity = fileService.queryFileInfoById(fileUuid);
        if (entity == null) {
            logger.error("文件不存在，无法生成分享链接：" + fileUuid);
            return null;
        }

        TemporarySignatureRequest request = new TemporarySignatureRequest();
        request.setBucketName(bucketName);
        request.setObjectKey(entity.getFilePath());
        request.setRequestDate(new Date());
        request.setExpires(60 * 60); // 默认有效期1小时（秒）

        try {
            TemporarySignatureResponse signature = getObsClient().createTemporarySignature(request);
            return signature.getSignedUrl();
        } catch (ObsException e) {
            logger.error("生成分享链接失败：" + e.getMessage(), e);
            return null;
        }
    }

    /**
     * 生成带自定义有效期的分享链接
     */
    @Override
    public String queryShareUrl(String fileUuid, Integer fileValidity) {
        PtFileEntity entity = fileService.queryFileInfoById(fileUuid);
        if (entity == null) {
            logger.error("文件不存在，无法生成带有效期的分享链接：" + fileUuid);
            return null;
        }

        // 校验有效期参数
        if (fileValidity == null || fileValidity <= 0) {
            logger.warn("无效的有效期参数，使用默认值1天");
            fileValidity = 1;
        }

        TemporarySignatureRequest request = new TemporarySignatureRequest();
        request.setBucketName(bucketName);
        request.setObjectKey(entity.getFilePath());
        request.setRequestDate(new Date());
        request.setExpires(60 * 60 * 24 * fileValidity); // 转换为秒（天→小时→分钟→秒）

        try {
            TemporarySignatureResponse signature = getObsClient().createTemporarySignature(request);
            return signature.getSignedUrl();
        } catch (ObsException e) {
            logger.error("生成带有效期的分享链接失败：" + e.getMessage(), e);
            return null;
        }
    }

    /**
     * 批量移动文件到目标文件夹
     */
    @Override
    public void moveFiles(List<String> fileUuids, String targetFolderUuid) {
        // 参数校验
        if (fileUuids == null || fileUuids.isEmpty()) {
            throw new IllegalArgumentException("待移动文件列表不能为空");
        }
        if (targetFolderUuid == null || targetFolderUuid.trim().isEmpty()) {
            throw new IllegalArgumentException("目标文件夹ID不能为空");
        }

        // 验证目标文件夹合法性
        PtFileEntity targetFolder = fileService.queryFileInfoById(targetFolderUuid);
        if (targetFolder == null) {
            throw new RuntimeException("目标文件夹不存在：" + targetFolderUuid);
        }
        if (targetFolder.getFileType() != FILE_TYPE_FOLDER) {
            throw new RuntimeException("目标不是文件夹：" + targetFolderUuid);
        }

        // 处理目标路径（确保以分隔符结尾）
        String targetPath = targetFolder.getFilePath();
        if (!targetPath.endsWith(FILE_SEPARATOR)) {
            targetPath += FILE_SEPARATOR;
        }

        ObsClient obsClient = getObsClient();
        try {
            // 遍历移动每个文件
            for (String fileUuid : fileUuids) {
                PtFileEntity file = fileService.queryFileInfoById(fileUuid);
                if (file == null) {
                    logger.warn("文件不存在，跳过移动：" + fileUuid);
                    continue;
                }

                // 暂不支持文件夹移动（如需支持需递归处理子文件）
                if (file.getFileType() == FILE_TYPE_FOLDER) {
                    logger.warn("不支持移动文件夹，跳过：" + fileUuid);
                    continue;
                }

                // 构建原路径和新路径
                String oldPath = file.getFilePath();
                String newPath = targetPath + file.getFileName();

                // 跳过同路径移动
                if (oldPath.equals(newPath)) {
                    logger.info("文件已在目标路径，无需移动：" + oldPath);
                    continue;
                }

                // 执行OBS移动（复制+删除原文件）
                obsClient.copyObject(bucketName, oldPath, bucketName, newPath);
                obsClient.deleteObject(bucketName, oldPath);
                logger.info("文件移动成功（OBS）：" + oldPath + " -> " + newPath);
            }

            // 更新数据库中文件的父目录关联
            fileService.updateParentUuids(fileUuids, targetFolderUuid);
            logger.info("批量更新文件父目录成功，共" + fileUuids.size() + "个文件");

            // 同步数据到数据库
            syncFileData();
        } catch (ObsException e) {
            logger.error("OBS操作失败：" + e.getMessage(), e);
            throw new RuntimeException("文件移动失败（OBS错误）：" + e.getMessage());
        } catch (Exception e) {
            logger.error("移动文件异常：" + e.getMessage(), e);
            throw new RuntimeException("文件移动失败：" + e.getMessage());
        }
    }

    /**
     * 工具方法：判断路径是否为文件夹（路径以分隔符结尾）
     */
    private boolean isFolder(String filePath) {
        if (filePath == null || filePath.length() == 0) {
            return false;
        }
        String lastStr = filePath.substring(filePath.length() - 1);
        return FILE_SEPARATOR.equals(lastStr);
    }

    /**
     * 工具方法：从路径中提取文件名
     */
    private String getFileName(String filePath) {
        if (filePath == null) {
            return "";
        }
        // 路径包含分隔符时，取最后一段
        if (filePath.indexOf(FILE_SEPARATOR) != -1) {
            String[] fileArray = filePath.split(FILE_SEPARATOR);
            if (isFolder(filePath)) {
                // 文件夹名称带分隔符
                return fileArray[fileArray.length - 1] + FILE_SEPARATOR;
            } else {
                // 文件名不带分隔符
                return fileArray[fileArray.length - 1];
            }
        }
        // 路径无分隔符时，直接返回
        return filePath;
    }
}